package ewu

import ewu._

/**
 * Diese Klasse stellt den Multiplikationsoperator zur Verfuegung.
 * Es werden die beiden Expressions "left" sowie "right"
 * verwendet.
 * Beispiel: left * right
 */

case class Mult(left: Expression,
                right: Expression) extends Operator {

  def evaluate(env: Environment): Option[BigInt]
  = (left.evaluate(env), right.evaluate(env)) match {
    case (Some(le), Some(ri)) => Some(le * ri)
    case _ => None
  }

  override def toString: String = (left, right) match{
    //case (o: Operand, n: Name)
    //  => toString(left) + toString(right)
    case _
      => toString(left) + "*" + toString(right)
  }

  private def toString (e: Expression): String = e match{
    case _: Plus  => "(" + e + ")"
    case _: Minus => "(" + e + ")"
    case _        => e.toString
  }

  def reduce: Expression = (left.reduce, right.reduce) match {
    case (Number(v), _) if v == 0 => Number(0)
    case (_, Number(v)) if v == 0 => Number(0)
    case (Number(v), e) if v == 1 => e
    case (e, Number(v)) if v == 1 => e
    case (le: Number, ri: Number) => Number(le.value * ri.value)
    case (le: PowerOf, ri) if le equals ri
      => PowerOf(le.left, Mult(Number(2), le.right)).reduce
    case (le, ri) if le equals ri
      => PowerOf(le, Number(2)).reduce
    case (e, n: Number)           => Mult(n, e).reduce
    case (le, ri)                 => reduceMult(le, ri)
  }

  private def reduceMult (left: Expression,
                          right: Expression): Expression = {
    val list =
      getNumbers(getNames(toList(left):::toList(right), Nil), 1)
    if (list.last == Number(0)) Number(0)
    else toExpression(list)
  }

  private def toList (e: Expression): List[Expression] =
      e match {
    case m: Mult => toList(m.left):::toList(m.right)
    case _ => e::Nil
  }

  private def getNames (list: List[Expression],
                        names: List[PowerOf]):
        (List[Expression]) = list match {
    case ((e: Name)::xs)
      => getNames(xs, insertName(names, (PowerOf(e, Number(1)))))
    case ((PowerOf(e:Name, e2))::xs)
      => getNames(xs, insertName(names, PowerOf(e, e2)))
    case (x::xs)         =>  x::getNames(xs, names)
    case _               => namesToList(names)
  }

  private def insertName (names: List[PowerOf], p: PowerOf):
        List[PowerOf] = (names, p) match{
    case (PowerOf(n1, e1)::xs, PowerOf(n2, e2)) if n1 == n2
      => (PowerOf(n1, Plus(e1, e2).reduce)::xs)
    case (x::xs, _) => x::insertName(xs, p)
    case _          => p::Nil
  }

  private def namesToList (names: List[PowerOf]):
        List[Expression] = names match {
    case (PowerOf(e, Number(v))::xs) if v == 1
      => e::namesToList(xs)
    case (PowerOf(e, v: Number)::xs) if v != 0
      => PowerOf(e, Number(v.value))::namesToList(xs)
    case (x::xs)                     => Number(1)::namesToList(xs)
    case _                           => Nil
  }

  private def getNumbers (list: List[Expression], count: BigInt):
        (List[Expression]) = list match {
    case ((n: Number)::xs) => getNumbers(xs, count * n.value)
    case (x::xs)           => x::getNumbers(xs, count)
    case _ if count == 0   => Number(0)::Nil
    case _
      => if (count != 1) Number(count)::Nil else Nil
  }

  private def toExpression (list: List[Expression]): Expression =
      list match {
    case (x::y::xs) => Mult(x, toExpression(y::xs))
    case (y::xs)    => y
    case _          => null
  }
}//end Mult